function checkAttachFileIdOrFunctionId(variableName) {
  const regExp = /@([0-9a-fA_F]{24})$/;
  return regExp.exec(variableName);
}
function getAttachFileIdOrFunctionId(variableName) {
  const regExp = /@([0-9a-fA_F]{24})$/;
  if (!checkAttachFileIdOrFunctionId(variableName)) {
    return null;
  }
  return variableName.match(regExp)[1];
}
function getOriginalName(variableName) {
  const regExp = /@([0-9a-fA_F]{24})$/;
  const formatArray = variableName.match(regExp);
  if (formatArray) {
    return variableName.substr(0, variableName.length - 25);
  }
  return variableName;
}
function handleStaticsOutputFunctionId(functionId, variableName) {
  let newVariableName = variableName;
  let funcId;
  if (checkAttachFileIdOrFunctionId(variableName)) {
    funcId = getAttachFileIdOrFunctionId(variableName);
  }
  // 来自于被测函数的局部静态变量,被测函数初始化解析时局部静态变量variable没有@functionId
  if (funcId && funcId === functionId.toString()) {
    newVariableName = getOriginalName(variableName);
  }
  return newVariableName;
}
function handleGlobalOutputFileId(fileId, variableName) {
  let newVariableName = variableName;
  let variableFileId;
  if (checkAttachFileIdOrFunctionId(variableName)) {
    variableFileId = getAttachFileIdOrFunctionId(variableName);
  }
  // 来自于被测函数的局部静态变量,被测函数初始化解析时局部静态变量variable没有@functionId
  if (variableFileId && variableFileId === fileId.toString()) {
    newVariableName = variableName.replace(checkAttachFileIdOrFunctionId(variableName)[0], '');
  }
  return newVariableName;
}
/**
 * 处理集成测试全局变量variableName
 * @param {Object} globalVariables 需要处理的全局白能量数据
 * @param {Object}  file 当前变量文件
 * @param {String}  entryFileId 入口函数所在文件id
 * @param {Boolean} attachFile 是否拼接文件
 * @param {Object} isEntryFunc 是否入口函数
 * @param {Object} allFilePathOfVersion 当前版本所有文件map key是filePath
 * @param {Object} allFileIdsOfVersion 当前版本所有文件map key是fileId
 * @param {string}  type 操作类型：
 *                           attachFileId:拼接文件id;
 *                           idToPath:文件id转成文件路径;
 *                           pathToId:文件路径转成文件id。
 *                           output:输出检查项
 *
 */
function handleGlobals(globalVariables, {
  file,
  entryFileId,
  type = 'attachFileId',
  attachFile = true,
  isEntryFunc = false,
  allFilePathOfVersion,
  allFileIdsOfVersion,
} = {}) {
  Object.keys(globalVariables).forEach((variableName) => {
    let newVariableName = variableName;
    switch (type) {
      case 'attachFileId': {
        let originalName = variableName;
        // 入口函数全局变量默认文件发生变化
        let fileId = file._id.toString();
        let filePath = file.path;
        if (isEntryFunc
          && globalVariables[variableName].fileId
          && globalVariables[variableName].filePath
          && globalVariables[variableName].fileId !== fileId
          && allFileIdsOfVersion
          && allFileIdsOfVersion.has(globalVariables[variableName].fileId)) {
          fileId = globalVariables[variableName].fileId;
          filePath = globalVariables[variableName].filePath;
        }
        const attributes = globalVariables[variableName]['@attributes'];
        // 入口函数的全局变量variableName不拼接fileId，参与集成的函数的静态全局变量variableName拼接变量选用的fileId
        if (attachFile
          && !checkAttachFileIdOrFunctionId(variableName)
          && attributes
          && attributes.includes('isStatic')) {
          newVariableName += `@${fileId}`;
        }
        if (checkAttachFileIdOrFunctionId(variableName)) {
          originalName = variableName.replace(checkAttachFileIdOrFunctionId(variableName)[0], '');
        }
        globalVariables[newVariableName] = globalVariables[variableName];
        globalVariables[newVariableName].fileId = fileId;
        globalVariables[newVariableName].filePath = filePath;
        globalVariables[newVariableName].isOriginal = isEntryFunc;
        globalVariables[newVariableName].originalName = originalName;
        globalVariables[newVariableName]['@name'] = globalVariables[variableName]['@name'] || variableName;
        if (variableName !== newVariableName) {
          delete globalVariables[variableName];
        }
        break;
      }
      case 'idToPath': {
        if (entryFileId && globalVariables[variableName].fileId && globalVariables[variableName].fileId.toString() === entryFileId.toString()) {
          delete globalVariables[variableName].fileId;
          delete globalVariables[variableName].filePath;
        }
        if (checkAttachFileIdOrFunctionId(variableName)) {
          const fileId = getAttachFileIdOrFunctionId(variableName);
          const file = allFileIdsOfVersion.get(fileId);
          if (!file) {
            delete globalVariables[variableName];
            return;
          }
          newVariableName = variableName.replace(fileId, file.path);
          globalVariables[newVariableName] = globalVariables[variableName];
          globalVariables[newVariableName].filePath = file.path;
          delete globalVariables[variableName];
        }
        break;
      }
      case 'pathToId': {
        let { filePath } = globalVariables[variableName];
        let { fileId } = globalVariables[variableName];
        if (filePath && !allFilePathOfVersion.has(filePath)) {
          delete globalVariables[variableName].fileId;
          delete globalVariables[variableName].filePath;
        }
        if (filePath && allFilePathOfVersion.has(filePath) && fileId.toString() !== allFilePathOfVersion.get(filePath)._id.toString()) {
          globalVariables[variableName].fileId = allFilePathOfVersion.get(filePath)._id.toString();
        }
        if (variableName.indexOf('@') === -1) {
          return;
        }
        [, filePath] = variableName.split('@');
        if (!filePath) {
          delete globalVariables[variableName];
          return;
        }
        const file = allFilePathOfVersion.get(filePath);
        if (!file) {
          delete globalVariables[variableName];
          return;
        }
        fileId = file._id.toString();
        newVariableName = variableName.replace(filePath, fileId);
        const originalVariableObj = globalVariables[variableName];
        delete globalVariables[variableName];
        globalVariables[newVariableName] = originalVariableObj;
        globalVariables[newVariableName].fileId = fileId;
        break;
      }
      case 'output': {
        newVariableName = handleGlobalOutputFileId(entryFileId, variableName);
        globalVariables[newVariableName] = globalVariables[variableName];
        delete globalVariables[variableName];
        break;
      }
      default: break;
    }
  });
  return globalVariables;
}
/**
 * 处理集成测试局部静态变量
 * @param {Object} staticVariables
 * @param {String}  entryFunctionId 入口函数id
 * @param {Object} allFuncIdsMap 所有函数map
 * @param {Boolean} isEntryFunc 是否入口函数
 * @param {Object} allFilePathOfVersion 当前版本所有文件map key是filePath
 * @param {Object} allFileIdsOfVersion 当前版本所有文件map key是fileId
 * @param {Object} allFuncsOfFileAndMangledId 当前版本所有函数map key是fileId-mangledId
 * @param {String} type  操作类型：
 *                           attachFuncId:拼接函数id;
 *                           idToPath:函数id转成路径;
 *                           pathToId:路径转成函数id;
 *                           output:输出检查项保存。
 */
function handleStatics(staticVariables, {
  entryFunctionId,
  type = 'attachFuncId',
  isEntryFunc = false,
  allFuncIdsMap,
  allFileIdsOfVersion,
  allFuncsOfFileAndMangledId,
  allFilePathOfVersion,
}) {
  Object.keys(staticVariables).forEach((variableName) => {
    let newVariableName = variableName;
    // 变量来源文件的原始名
    let originalName = variableName;
    switch (type) {
      case 'attachFuncId': {
        if (staticVariables[variableName].functionId) {
          const functionId = staticVariables[variableName].functionId.toString();
          const func = allFuncIdsMap.get(functionId);
          if (!func) {
            delete staticVariables[variableName];
            return;
          }
          const fileId = func.fileId.toString();
          const file = allFileIdsOfVersion.get(fileId);
          if (!file) {
            delete staticVariables[variableName];
            return;
          }
          if (!checkAttachFileIdOrFunctionId(variableName)) {
            newVariableName += `@${functionId}`;
            staticVariables[newVariableName] = staticVariables[variableName];
          } else {
            originalName = variableName.replace(checkAttachFileIdOrFunctionId(variableName)[0], '');
          }
          staticVariables[newVariableName].isOriginal = isEntryFunc;
          staticVariables[newVariableName].fileId = fileId;
          staticVariables[newVariableName].filePath = file.path;
          staticVariables[newVariableName].originalName = originalName;
          staticVariables[newVariableName].functionName = func.functionName;
          if (variableName !== newVariableName) {
            delete staticVariables[variableName];
          }
        }
        break;
      }
      case 'idToPath': {
        if (checkAttachFileIdOrFunctionId(variableName)) {
          const functionId = getAttachFileIdOrFunctionId(variableName);
          const func = allFuncIdsMap.get(functionId);
          if (!func) {
            delete staticVariables[variableName];
            return;
          }
          const file = allFileIdsOfVersion.get(func.fileId.toString());
          if (!file) {
            delete staticVariables[variableName];
            return;
          }
          newVariableName = variableName.replace(functionId, `${file.path}@${func.mangledId}`);
          staticVariables[newVariableName] = staticVariables[variableName];
          delete staticVariables[variableName];
        }
        break;
      }
      case 'pathToId': {
        if (variableName.indexOf('@') !== -1) {
          const splitVariableNames = variableName.split('@');
          const filePath = staticVariables[variableName].filePath ? staticVariables[variableName].filePath : splitVariableNames[1];
          const mangledId = splitVariableNames[2];
          const file = allFilePathOfVersion.get(filePath);
          if (!file) {
            delete staticVariables[variableName];
            return;
          }
          const func = allFuncsOfFileAndMangledId.get(`${file._id.toString()}-${mangledId}`);
          if (!func) {
            delete staticVariables[variableName];
            return;
          }
          const funcId = func._id.toString();
          newVariableName = variableName.replace(`${filePath}@${mangledId}`, funcId);
          staticVariables[newVariableName] = staticVariables[variableName];
          staticVariables[newVariableName].fileId = file._id.toString();
          delete staticVariables[variableName];
        }
        break;
      }
      case 'output': {
        newVariableName = handleStaticsOutputFunctionId(entryFunctionId, variableName);
        staticVariables[newVariableName] = staticVariables[variableName];
        delete staticVariables[variableName];
        break;
      }
      default: break;
    }
  });
  return staticVariables;
}
/**
 * 处理集成测试绝对地址表达式
 * @param {Object} fixedAddressVariables
 * @param {Object} allFuncIdsMap 所有函数map
 * @param {Object} allFilePathOfVersion 当前版本所有文件map key是filePath
 * @param {Object} allFileIdsOfVersion 当前版本所有文件map key是fileId
 * @param {Object} allFuncsOfFileAndMangledId 当前版本所有函数map key是fileId-mangledId
 * @param {String} type  操作类型：
 *                           idToPath:函数id转成路径;
 *                           pathToId:路径转成函数id;
 */
function handleFixedAddressExprs(fixedAddressExprsVariables, {
  type = 'attachFuncId',
  allFuncIdsMap,
  allFileIdsOfVersion,
  allFuncsOfFileAndMangledId,
  allFilePathOfVersion,
}) {
  Object.keys(fixedAddressExprsVariables).forEach((variableName) => {
    let newVariableName = variableName;
    switch (type) {
      case 'idToPath': {
        if (checkAttachFileIdOrFunctionId(variableName)) {
          const functionId = getAttachFileIdOrFunctionId(variableName);
          const func = allFuncIdsMap.get(functionId);
          if (!func) {
            delete fixedAddressExprsVariables[variableName];
            return;
          }
          const file = allFileIdsOfVersion.get(func.fileId.toString());
          if (!file) {
            delete fixedAddressExprsVariables[variableName];
            return;
          }
          newVariableName = variableName.replace(functionId, `${file.path}@${func.mangledId}`);
          fixedAddressExprsVariables[newVariableName] = fixedAddressExprsVariables[variableName];
          delete fixedAddressExprsVariables[variableName];
        }
        break;
      }
      case 'pathToId': {
        if (variableName.indexOf('@') === -1) {
          return;
        }
        const splitVariableNames = variableName.split('@');
        const filePath = fixedAddressExprsVariables[variableName].filePath
          ? fixedAddressExprsVariables[variableName].filePath : splitVariableNames[1];
        const mangledId = splitVariableNames[2];
        if (!filePath) {
          delete fixedAddressExprsVariables[variableName];
          return;
        }
        const file = allFilePathOfVersion.get(filePath);
        if (!file) {
          delete fixedAddressExprsVariables[variableName];
          return;
        }
        const fileId = file._id.toString();
        const originalVariableObj = fixedAddressExprsVariables[variableName];
        // 防止表达式中出现@特殊符号
        if (variableName.indexOf(`@${filePath}`) !== -1) {
          const func = allFuncsOfFileAndMangledId.get(`${fileId}-${mangledId}`);
          if (!func) {
            delete fixedAddressExprsVariables[variableName];
            return;
          }
          const funcId = func._id.toString();
          newVariableName = variableName.replace(`${filePath}@${mangledId}`, funcId);
          delete fixedAddressExprsVariables[variableName];
        }
        fixedAddressExprsVariables[newVariableName] = originalVariableObj;
        fixedAddressExprsVariables[newVariableName].fileId = fileId;
        break;
      }
      default: break;
    }
  });
  return fixedAddressExprsVariables;
}
/**
 * 处理集成测试绝对地址目标
 * @param {object} fixedAddressVariables
 * @param {Object} allFilePathOfVersion 当前版本所有文件map key是filePath
 * @param {String} type  操作类型：
 *                           idToPath:函数id转成路径;
 *                           pathToId:路径转成函数id;
 */
function handleFixedAddressBases(fixedAddressBasesVariables, {
  type = 'pathToId',
  allFilePathOfVersion,
}) {
  Object.keys(fixedAddressBasesVariables).forEach((variableName) => {
    switch (type) {
      case 'pathToId': {
        if (variableName.indexOf('@') === -1) {
          return;
        }
        const filePath = fixedAddressBasesVariables[variableName].filePath ? fixedAddressBasesVariables[variableName].filePath : variableName.split('@')[1];
        if (!filePath) {
          delete fixedAddressBasesVariables[variableName];
          return;
        }
        const file = allFilePathOfVersion.get(filePath);
        if (!file) {
          delete fixedAddressBasesVariables[variableName];
          return;
        }
        fixedAddressBasesVariables[variableName].fileId = file._id.toString();
        break;
      }
      default: break;
    }
  });
  return fixedAddressBasesVariables;
}
/**
 *处理桩函数
 * @param {Object} stubs 桩函数信息
 * @param {Object} allFilePathOfVersion 当前版本所有文件map key是filePath
 * @param {Object} allFileIdsOfVersion 当前版本所有文件map key是fileId
 * @param {String} type
 *                idToPath:文件id转成路径;
 *                pathToId:路径转成文件id;
 *
 */
function handleStubs(stubs, {
  type = 'idToPath',
  allFilePathOfVersion,
  allFileIdsOfVersion,
}) {
  Object.keys(stubs).forEach((variableName) => {
    let newVariableName = variableName;
    switch (type) {
      case 'idToPath': {
        if (!checkAttachFileIdOrFunctionId(variableName)) {
          return;
        }
        const fileId = getAttachFileIdOrFunctionId(variableName);
        const file = allFileIdsOfVersion.get(fileId);
        if (!file) {
          delete stubs[variableName];
          return;
        }
        // 拼接在key中的文件和属性中的文件id不一致
        if (stubs[variableName].fileId && fileId !== stubs[variableName].fileId) {
          delete stubs[variableName];
          return;
        }
        newVariableName = variableName.replace(fileId, file.path);
        stubs[newVariableName] = stubs[variableName];
        stubs[newVariableName].filePath = file.path;
        if (variableName !== newVariableName) {
          delete stubs[variableName];
        }
        break;
      }
      case 'pathToId': {
        if (variableName.indexOf('@') === -1 && !stubs[variableName].filePath) {
          return;
        }
        let attachFilePath;
        // 兼容老用例文件格式
        if (variableName.indexOf('@') > 0) {
          attachFilePath = variableName.substring(variableName.lastIndexOf('@') + 1, variableName.length);
        }
        const filePath = stubs[variableName].filePath ? stubs[variableName].filePath : attachFilePath;
        if (!filePath) {
          delete stubs[variableName];
          return;
        }
        const file = allFilePathOfVersion.get(filePath);
        if (!file) {
          delete stubs[variableName];
          return;
        }
        const fileId = file._id.toString();
        if (attachFilePath) {
          newVariableName = variableName.replace(attachFilePath, fileId);
        }
        stubs[newVariableName] = stubs[variableName];
        if (variableName !== newVariableName) {
          delete stubs[variableName];
        }
        // 删除测试用例冗余的老文件数据
        delete stubs[newVariableName].fileId;
        delete stubs[newVariableName].filePath;
        break;
      }
      default: break;
    }
  });
  return stubs;
}/**
 * 处理指针目标变量
 * @param {Object} mallocVariables
 * @param {Boolean} isEntryFunc 是否入口函数
 * @param {Object} allFilePathOfVersion 当前版本所有文件map key是filePath
 * @param {String} type
 */
function handleMallocs(mallocVariables, {
  type = 'attachFileId',
  file,
  isEntryFunc = false,
  allFilePathOfVersion,
}) {
  Object.keys(mallocVariables).forEach((variableName) => {
    switch (type) {
      case 'attachFileId': {
        mallocVariables[variableName].fileId = mallocVariables[variableName].fileId || file._id.toString();
        mallocVariables[variableName].filePath = mallocVariables[variableName].filePath || file.path;
        mallocVariables[variableName].isOriginal = isEntryFunc;
        mallocVariables[variableName]['@name'] = variableName;
        break;
      }
      case 'pathToid': {
        if (variableName.indexOf('@') === -1) {
          return;
        }
        const filePath = mallocVariables[variableName].filePath ? mallocVariables[variableName].filePath : variableName.split('@')[1];
        if (!filePath) {
          delete mallocVariables[variableName];
          return;
        }
        const file = allFilePathOfVersion.get(filePath);
        if (!file) {
          delete mallocVariables[variableName];
          return;
        }
        mallocVariables[variableName].fileId = file._id.toString();
        break;
      }
      default: break;
    }
  });
  return mallocVariables;
}
module.exports = {
  checkAttachFileIdOrFunctionId,
  handleGlobals,
  handleStatics,
  handleStubs,
  handleMallocs,
  handleStaticsOutputFunctionId,
  handleGlobalOutputFileId,
  handleFixedAddressExprs,
  handleFixedAddressBases,
  getAttachFileIdOrFunctionId,
  getOriginalName,
};
